using Microsoft.Extensions.Caching.Distributed;
using StackExchange.Redis;
using System.Text.Json;
using WeatherSystem.API.Configurations;

namespace WeatherSystem.API.Services
{
    /// <summary>
    /// Enhanced distributed cache service interface
    /// </summary>
    public interface IDistributedCacheService : ICacheService
    {
        Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default) where T : class;
        Task SetAsync<T>(string key, T value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) where T : class;
        Task RemoveByTagAsync(string tag, CancellationToken cancellationToken = default);
        Task SetWithTagsAsync<T>(string key, T value, string[] tags, TimeSpan? expiration = null, CancellationToken cancellationToken = default) where T : class;
    }

    /// <summary>
    /// Enhanced distributed cache service implementation
    /// </summary>
    public class DistributedCacheService : IDistributedCacheService
    {
        private readonly IDistributedCache _distributedCache;
        private readonly IConnectionMultiplexer? _redis;
        private readonly ILogger<DistributedCacheService> _logger;
        private readonly IBusinessMetricsService _metricsService;
        private readonly JsonSerializerOptions _jsonOptions;

        public DistributedCacheService(
            IDistributedCache distributedCache,
            IConnectionMultiplexer? redis,
            ILogger<DistributedCacheService> logger,
            IBusinessMetricsService metricsService)
        {
            _distributedCache = distributedCache;
            _redis = redis;
            _logger = logger;
            _metricsService = metricsService;
            _jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                WriteIndented = false
            };
        }

        public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default) where T : class
        {
            try
            {
                var value = await _distributedCache.GetStringAsync(key, cancellationToken);
                var hit = value != null;
                _metricsService.RecordCacheOperation("get", "distributed", hit);
                return value == null ? null : JsonSerializer.Deserialize<T>(value, _jsonOptions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "从分布式缓存获取数据失败，键: {Key}", key);
                return null;
            }
        }

        public async Task SetAsync<T>(string key, T value, TimeSpan? expiration = null, CancellationToken cancellationToken = default) where T : class
        {
            try
            {
                var options = new DistributedCacheEntryOptions();
                if (expiration.HasValue)
                {
                    options.AbsoluteExpirationRelativeToNow = expiration;
                }

                var json = JsonSerializer.Serialize(value, _jsonOptions);
                await _distributedCache.SetStringAsync(key, json, options, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置分布式缓存失败，键: {Key}", key);
                throw;
            }
        }

        public async Task RemoveAsync(string key, CancellationToken cancellationToken = default)
        {
            try
            {
                await _distributedCache.RemoveAsync(key, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除分布式缓存失败，键: {Key}", key);
            }
        }

        public async Task RemovePatternAsync(string pattern, CancellationToken cancellationToken = default)
        {
            if (_redis == null)
            {
                _logger.LogWarning("Redis 连接不可用，无法删除匹配模式的缓存项：{Pattern}", pattern);
                return;
            }

            try
            {
                var database = _redis.GetDatabase();
                var server = _redis.GetServer(_redis.GetEndPoints().First());

                await foreach (var key in server.KeysAsync(pattern: pattern))
                {
                    await database.KeyDeleteAsync(key);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除匹配模式的缓存项失败，模式: {Pattern}", pattern);
            }
        }

        public async Task RemoveByPrefixAsync(string prefix, CancellationToken cancellationToken = default)
        {
            await RemovePatternAsync($"{prefix}*", cancellationToken);
        }

        public async Task ClearAsync(CancellationToken cancellationToken = default)
        {
            if (_redis == null)
            {
                _logger.LogWarning("Redis 连接不可用，无法清空缓存");
                return;
            }

            try
            {
                var database = _redis.GetDatabase();
                var server = _redis.GetServer(_redis.GetEndPoints().First());

                await foreach (var key in server.KeysAsync())
                {
                    await database.KeyDeleteAsync(key);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "清空缓存失败");
            }
        }

        public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> getItem, TimeSpan? expiration = null, CancellationToken cancellationToken = default) where T : class
        {
            var cachedValue = await GetAsync<T>(key, cancellationToken);
            if (cachedValue != null)
            {
                return cachedValue;
            }

            var item = await getItem();
            await SetAsync(key, item, expiration, cancellationToken);
            return item;
        }

        // ICacheService 接口的兼容实现（不带 CancellationToken）
        public Task<T?> GetAsync<T>(string key) where T : class
        {
            return GetAsync<T>(key, CancellationToken.None);
        }

        public Task SetAsync<T>(string key, T value, TimeSpan? expiration = null) where T : class
        {
            return SetAsync<T>(key, value, expiration, CancellationToken.None);
        }

        public Task RemoveAsync(string key)
        {
            return RemoveAsync(key, CancellationToken.None);
        }

        public Task RemovePatternAsync(string pattern)
        {
            return RemovePatternAsync(pattern, CancellationToken.None);
        }

        public async Task<bool> ExistsAsync(string key)
        {
            return await ExistsAsync(key, CancellationToken.None);
        }

        public async Task<bool> ExistsAsync(string key, CancellationToken cancellationToken = default)
        {
            try
            {
                var cachedValue = await _distributedCache.GetAsync(key, cancellationToken);
                return cachedValue != null;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查缓存存在性失败，键: {Key}", key);
                return false;
            }
        }

        public Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> getItem, TimeSpan? expiration = null) where T : class
        {
            return GetOrSetAsync<T>(key, getItem, expiration, CancellationToken.None);
        }

        public async Task RemoveByTagAsync(string tag, CancellationToken cancellationToken = default)
        {
            if (_redis == null)
            {
                _logger.LogWarning("Redis 连接不可用，无法通过标签删除缓存项：{Tag}", tag);
                return;
            }

            try
            {
                var database = _redis.GetDatabase();
                var tagKey = $"tag:{tag}";
                var members = await database.SetMembersAsync(tagKey);

                if (members.Length > 0)
                {
                    var keys = members.Select(m => (RedisKey)m.ToString()).ToArray();
                    await database.KeyDeleteAsync(keys);
                    await database.KeyDeleteAsync(tagKey);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "通过标签删除缓存项失败，标签: {Tag}", tag);
            }
        }

        public async Task SetWithTagsAsync<T>(string key, T value, string[] tags, TimeSpan? expiration = null, CancellationToken cancellationToken = default) where T : class
        {
            await SetAsync(key, value, expiration, cancellationToken);

            if (_redis != null && tags.Length > 0)
            {
                try
                {
                    var database = _redis.GetDatabase();
                    var tasks = new List<Task>();

                    foreach (var tag in tags)
                    {
                        var tagKey = $"tag:{tag}";
                        tasks.Add(database.SetAddAsync(tagKey, key));
                    }

                    await Task.WhenAll(tasks);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "设置缓存标签失败，键: {Key}", key);
                }
            }
        }
    }

    /// <summary>
    /// Cache invalidation service for weather data
    /// </summary>
    public interface ICacheInvalidationService
    {
        Task InvalidateWeatherDataAsync(Guid cityId);
        Task InvalidateStatisticsAsync(Guid cityId);
        Task InvalidateAllWeatherDataAsync();
    }

    public class CacheInvalidationService : ICacheInvalidationService
    {
        private readonly IDistributedCacheService _cacheService;
        private readonly ILogger<CacheInvalidationService> _logger;

        public CacheInvalidationService(IDistributedCacheService cacheService, ILogger<CacheInvalidationService> logger)
        {
            _cacheService = cacheService;
            _logger = logger;
        }

        public async Task InvalidateWeatherDataAsync(Guid cityId)
        {
            try
            {
                await _cacheService.RemoveByTagAsync($"weather:{cityId}");
                await _cacheService.RemoveAsync($"weather:latest:{cityId}");
                await _cacheService.RemovePatternAsync($"weather:history:{cityId}:*");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "失效天气数据缓存失败，城市ID: {CityId}", cityId);
            }
        }

        public async Task InvalidateStatisticsAsync(Guid cityId)
        {
            try
            {
                await _cacheService.RemoveByTagAsync($"statistics:{cityId}");
                await _cacheService.RemovePatternAsync($"statistics:*:{cityId}:*");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "失效统计数据缓存失败，城市ID: {CityId}", cityId);
            }
        }

        public async Task InvalidateAllWeatherDataAsync()
        {
            try
            {
                await _cacheService.RemoveByTagAsync("weather");
                await _cacheService.RemoveByTagAsync("statistics");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "失效所有天气数据缓存失败");
            }
        }
    }
}